/*  create 4 table rows at a time, depending upon multby section */
uint32_t **galois_create_w32_lazy_split_rows(int i, int row_index)
{
	int p2, j, p1elt, p2elt, index, ishift, jshift;
	uint32_t **table_rows, *table;

	table_rows = (uint32_t **) malloc(sizeof(uint32_t *) * 4);

	for (j = 0; j < 4; j++) {
		table_rows[j] = (uint32_t *) malloc(sizeof(uint32_t) * (1 << 8));
		if (table_rows[j] == NULL) {
			for (j--; j>=0; j--) free(table_rows[j]);
			return NULL;
		}
	}

	ishift = 0;

	for (j = 0; j < 4; j++) {
		if (i+j > 3) {
			ishift = 8 * 3;
			jshift = 8 * (i+j-3);
		} else {
			jshift = (i + j) * 8;
		}
		table = table_rows[j];
		index = 0;
		p1elt = (row_index << ishift);
		for (p2 = 0; p2 < 256; p2++) {
			p2elt = (p2 << jshift);
			table[index] = galois_shift_multiply(p1elt, p2elt, 32);
			index++;
		}
	}

	return table_rows;
}

void galois_w32_lazy_split_region_multiply(unsigned char *region,
		int multby,
		int nbytes,
		unsigned char *r2,
		int add)
{
	uint32_t *ur1, *ur2;
	int i, j, a, b, accumulator, i8, j8, k;
	int acache[4];
	uint32_t **table_rows;
	unsigned char *copy;

	ur1 = (uint32_t *) region;
	ur2 = (r2 == NULL) ? ur1 : (uint32_t *) r2;
	nbytes /= sizeof(uint32_t);

	if (multby == 0) {
		if (!add) {
			bzero(r2, nbytes * sizeof(uint32_t));
		}
		return;
	}

	i8 = 0;
	for (i = 0; i < 4; i++) {
		acache[i] = (multby >> i8) & 255;
		i8 += 8;
	}
	if (r2 == NULL && !add) {
		copy = (unsigned char *) malloc(sizeof(unsigned char) * nbytes * sizeof(uint32_t));
		ur2 = (uint32_t *) copy;
		i8 = 0;
		for (i = 0; i < 4; i++) {
			if (acache[i] != 0) {
				table_rows = galois_create_w32_lazy_split_rows(i, acache[i]);
				if (table_rows == NULL) {
					printf("Cannot create table_rows for value %d\n", acache[i]);
					exit(1);
				}
				for (k = 0; k < nbytes; k++) {
					accumulator = 0;
					j8 = 0;
					for (j = 0; j < 4; j++) {
						b = (ur1[k] >> j8) & 255;
						accumulator ^= table_rows[j][b];
						j8 += 8;
					}
					if (i8 == 0) ur2[k] = accumulator;
					else ur2[k] ^= accumulator;
				}
				i8++;
				free(table_rows);
			}
		}
		free(region);
		region = copy;
	} else if (r2 == NULL && add) {
		copy = (unsigned char *) strdup((char *) region);
		ur2 = (uint32_t *) copy;
		for (i = 0; i < 4; i++) {
			if (acache[i] != 0) {
				table_rows = galois_create_w32_lazy_split_rows(i, acache[i]);
				if (table_rows == NULL) {
					printf("Cannot create table_rows for value %d\n", acache[i]);
					exit(1);
				}
				for (k = 0; k < nbytes; k++) {
					accumulator = 0;
					j8 = 0;
					for (j = 0; j < 4; j++) {
						b = (ur1[k] >> j8) & 255;
						accumulator ^= table_rows[j][b];
						j8 += 8;
					}
					ur2[k] ^= accumulator;
				}
				free(table_rows);
			}
		}
		free(region);
		region = copy;

	} else if (!add) {
		i8 = 0;
		for (i = 0; i < 4; i++) {
			if (acache[i] != 0) {
				table_rows = galois_create_w32_lazy_split_rows(i, acache[i]);
				if (table_rows == NULL) {
					printf("Cannot create table_rows for value %d\n", acache[i]);
					exit(1);
				}
				for (k = 0; k < nbytes; k++) {
					accumulator = 0;
					j8 = 0;
					for (j = 0; j < 4; j++) {
						b = (ur1[k] >> j8) & 255;
						accumulator ^= table_rows[j][b];
						j8 += 8;
					}
					if (i8 == 0) { ur2[k] = accumulator; }
					else ur2[k] ^= accumulator;
				}
				i8++;
				free(table_rows);
			}
		}
	} else {
		for (i = 0; i < 4; i++) {
			if (acache[i] != 0) {
				table_rows = galois_create_w32_lazy_split_rows(i, acache[i]);
				if (table_rows == NULL) {
					printf("Cannot create table_rows for value %d\n", acache[i]);
					exit(1);
				}
				for (k = 0; k < nbytes; k++) {
					accumulator = 0;
					j8 = 0;
					for (j = 0; j < 4; j++) {
						b = (ur1[k] >> j8) & 255;
						accumulator ^= table_rows[j][b];
						j8 += 8;
					}
					ur2[k] ^= accumulator;
				}
				free(table_rows);
			}
		}
	}
	return;
}

/* create one table row at a time, depending upon i, j, and multby section */
uint32_t *galois_create_w32_lazy_split_row(int i, int j, int row_index)
{
	int p2, index, ishift, jshift;
	uint32_t *table_row, p1elt, p2elt;

	table_row = (uint32_t *) malloc (sizeof(uint32_t) * (1 << 8));
	if (table_row == NULL) { return NULL; };

	if (i + j > 3) {
		ishift = 24;
		jshift = (i+j-3) * 8;
	} else {
		ishift = 0;
		jshift = (i+j) * 8;
	}
	p1elt = (row_index << ishift);
	index = 0;
	for (p2 = 0; p2 < 256; p2++) {
		p2elt = (p2 << jshift);
		table_row[index] = galois_shift_multiply(p1elt, p2elt, 32);
		index++;
	}

	return table_row;
}

void galois_w32_lazy_split_region_multiply(unsigned char *region,
		int multby,
		int nbytes,
		unsigned char *r2,
		int add)
{
	uint32_t *ur1, *ur2;
	int i, j, a, b, accumulator, i8, j8, k;
	int acache[4];
	uint32_t *table_row;
	unsigned char *copy;

	ur1 = (uint32_t *) region;
	ur2 = (r2 == NULL) ? ur1 : (uint32_t *) r2;
	nbytes /= sizeof(uint32_t);

	if (multby == 0) {
		if (!add) {
			bzero(r2, nbytes * sizeof(uint32_t));
		}
		return;
	}

	i8 = 0;
	for (i = 0; i < 4; i++) {
		acache[i] = (multby >> i8) & 255;
		i8 += 8;
	}
	if (r2 == NULL) {

		copy = (unsigned char *) malloc(sizeof(unsigned char) * sizeof(uint32_t) * nbytes);
		if (add) {
			memcpy(copy, region, nbytes * sizeof(uint32_t));
		} else {
			bzero(copy, nbytes * sizeof(uint32_t));
		}

		ur2 = (uint32_t *) copy;
		for (i = 0; i < 4; i++) {
			if (acache[i] != 0) {
				j8 = 0;
				for (j = 0; j < 4; j++) {
					table_row = galois_create_w32_lazy_split_row(i, j, acache[i]);
					for (k = 0; k < nbytes; k++) {
						b = ((ur1[k] >> j8) & 255);
						ur2[k] = ur2[k] ^ table_row[b];
					}
					free(table_row);
					j8 += 8;
				}
			}
		}
		free(region);
		region = copy;
	} else if (!add) {
		i8 = 0;
		for (i = 0; i < 4; i++) {
			if (acache[i] != 0) {
				j8 = 0;
				for (j = 0; j < 4; j++) {
					table_row = galois_create_w32_lazy_split_row(i, j, acache[i]);
					for (k = 0; k < nbytes; k++) {
						b = ((ur1[k] >> j8) & 255);
						if (i8 == 0) ur2[k] = table_row[b];
						else ur2[k] = ur2[k] ^ table_row[b];
					}
					free(table_row);
					j8 += 8;
					i8++;
				}
			}
		}
	} else {
		for (i = 0; i < 4; i++) {
			if (acache[i] != 0) {
				j8 = 0;
				for (j = 0; j < 4; j++) {
					table_row = galois_create_w32_lazy_split_row(i, j, acache[i]);
					for (k = 0; k < nbytes; k++) {
						b = ((ur1[k] >> j8) & 255);
						ur2[k] = ur2[k] ^ table_row[b];
					}
					free(table_row);
					j8 += 8;
				}
			}
		}
	}
}

